package order

import (
	"github.com/gin-gonic/gin"
	"github.com/opentracing/opentracing-go"
	"github.com/smartwalle/alipay/v3"
	"go.uber.org/zap"
	"net/http"
	"strconv"
	"web/order/api"
	"web/order/forms"
	"web/order/global"
	"web/order/proto"
)

// 获取订单列表
func GetOrderList(ctx *gin.Context) {
	// 获取本次请求的起始span
	topSpan := api.GetTopSpan(ctx)

	// 在起始span下创建子span——获取请求参数
	getArgsSpan := opentracing.StartSpan("获取请求参数", opentracing.ChildOf(topSpan.Context()))
	pages := ctx.DefaultQuery("pn", "0")
	pagesInt, _ := strconv.Atoi(pages)

	perNums := ctx.DefaultQuery("pnum", "0")
	perNumsInt, _ := strconv.Atoi(perNums)
	// "子span——获取请求参数"结束
	getArgsSpan.Finish()

	// 由于当前服务在发起grpc请求时配置了处理链路追踪的客户端拦截器，所以每次发起grpc调用前，
	// 只需使用ContextWithSpan方法得到一个携带父span的context（注意这个是context.Context类型，而不是gin.Context类型），
	// 再把这个携带父span的context写到grpc的请求参数中即可。
	grpcCtxWithSpan := opentracing.ContextWithSpan(ctx, topSpan)
	rsp, err := global.OrderSrvClient.OrderList(grpcCtxWithSpan, &proto.OrderFilterRequest{
		Pages:       int32(pagesInt),
		PagePerNums: int32(perNumsInt),
	})
	if err != nil {
		zap.S().Error("获取订单列表失败：", err)
		api.HandleGrpcErrorToHttp(err, ctx)
		return
	}

	ctx.JSON(http.StatusOK, rsp)
}

// 获取订单详情
func GetOrderDetail(ctx *gin.Context) {
	// 获取本次请求的起始span
	topSpan := api.GetTopSpan(ctx)

	idStr := ctx.Param("id")
	idInt, err := strconv.Atoi(idStr)
	if err != nil {
		zap.S().Error("strconv.Atoi：", err)
		ctx.JSON(http.StatusNotFound, gin.H{
			"msg": "解析参数出错，请检查订单id参数",
		})
		return
	}

	grpcCtxWithSpan := opentracing.ContextWithSpan(ctx, topSpan)
	rsp, err := global.OrderSrvClient.OrderDetail(grpcCtxWithSpan, &proto.OrderRequest{
		Id: int32(idInt),
	})
	if err != nil {
		zap.S().Error("获取订单详情失败：", err)
		api.HandleGrpcErrorToHttp(err, ctx)
		return
	}

	var g []gin.H
	for _, v := range rsp.Goods {
		g = append(g, gin.H{
			"id":    v.GoodsId,
			"name":  v.GoodsName,
			"image": v.GoodsImage,
			"price": v.GoodsPrice,
			"nums":  v.Nums,
		})
	}

	// 返回支付宝的支付url

	// 第三个参数表示是否为开发环境
	client, err := alipay.New(global.ServerConfig.AlipayInfo.AppId, global.ServerConfig.AlipayInfo.PrivateKey, false)
	if err != nil {
		zap.S().Error("实例化支付对象失败：", err)
		ctx.JSON(http.StatusInternalServerError, gin.H{
			"msg": "内部错误: 构建请求支付宝的url失败",
		})
		return
	}
	err = client.LoadAliPayPublicKey(global.ServerConfig.AlipayInfo.AliPublicKey)
	if err != nil {
		zap.S().Error("加载支付宝的公钥失败：", err)
		ctx.JSON(http.StatusInternalServerError, gin.H{
			"msg": "内部错误: 加载支付宝的公钥失败",
		})
		return
	}

	// 需要生成的支付url是页面支付
	p := alipay.TradePagePay{}
	// 回调url，应用请求支付宝成功后，支付宝会向应用发送一个回调请求，
	// 这个请求的ip必须是公网ip，所以应用需要使用内网穿透技术将本地运行ip+端口映射到公网
	p.NotifyURL = global.ServerConfig.AlipayInfo.NotifyUrl
	// 支付成功后的跳转页面
	p.ReturnURL = global.ServerConfig.AlipayInfo.ReturnUrl
	// 订单名称
	p.Subject = "优果生鲜订单-" + rsp.OrderInfo.OrderSn
	// 唯一订单id
	p.OutTradeNo = rsp.OrderInfo.OrderSn
	// 订单中需支付的总金额
	p.TotalAmount = strconv.FormatFloat(float64(rsp.OrderInfo.Total), 'f', 2, 64)
	// 网页支付必须这样来写
	p.ProductCode = "FAST_INSTANT_TRADE_PAY"

	url, err := client.TradePagePay(p)
	if err != nil {
		zap.S().Error("生成支付url失败：", err)
		ctx.JSON(http.StatusInternalServerError, gin.H{
			"msg": "内部错误: 生成支付url失败",
		})
		return
	}

	ctx.JSON(http.StatusOK, gin.H{
		"id":         rsp.OrderInfo.Id,
		"mobile":     rsp.OrderInfo.Mobile,
		"name":       rsp.OrderInfo.Name,
		"pay_type":   rsp.OrderInfo.PayType,
		"post":       rsp.OrderInfo.Post,
		"status":     rsp.OrderInfo.Status,
		"total":      rsp.OrderInfo.Total,
		"user":       rsp.OrderInfo.UserId,
		"address":    rsp.OrderInfo.Address,
		"goods":      g,
		"alipay_url": url.String(),
	})
}

// 创建订单
func CreateOrder(ctx *gin.Context) {
	f := new(forms.CreateOrderForm)
	if err := ctx.ShouldBind(&f); err != nil {
		api.HandleValidatorError(ctx, err)
		return
	}

	var userId int32
	if val, ok := ctx.Get("userId"); ok {
		userId = val.(int32)
	} else {
		ctx.JSON(http.StatusUnauthorized, gin.H{
			"msg": "请重新登录",
		})
		return
	}

	topSpan := api.GetTopSpan(ctx)
	grpcCtxWithSpan := opentracing.ContextWithSpan(ctx, topSpan)
	res, err := global.OrderSrvClient.CreateOrder(grpcCtxWithSpan, &proto.OrderRequest{
		UserId:  userId,
		Address: f.Address,
		Name:    f.Name,
		Mobile:  f.Mobile,
		Post:    f.Post,
	})
	if err != nil {
		api.HandleGrpcErrorToHttp(err, ctx)
		zap.S().Error("创建订单失败：", err)
		return
	}

	// 返回支付宝的支付url

	// 第三个参数表示是否为开发环境
	client, err := alipay.New(global.ServerConfig.AlipayInfo.AppId, global.ServerConfig.AlipayInfo.PrivateKey, false)
	if err != nil {
		zap.S().Error("实例化支付对象失败：", err)
		ctx.JSON(http.StatusInternalServerError, gin.H{
			"msg": "内部错误: 构建请求支付宝的url失败",
		})
		return
	}
	err = client.LoadAliPayPublicKey(global.ServerConfig.AlipayInfo.AliPublicKey)
	if err != nil {
		zap.S().Error("加载支付宝的公钥失败：", err)
		ctx.JSON(http.StatusInternalServerError, gin.H{
			"msg": "内部错误: 加载支付宝的公钥失败",
		})
		return
	}

	// 需要生成的支付url是页面支付
	p := alipay.TradePagePay{}
	// 回调url，应用请求支付宝成功后，支付宝会向应用发送一个回调请求，
	// 这个请求的ip必须是公网ip，所以应用需要使用内网穿透技术将本地运行ip+端口映射到公网
	p.NotifyURL = global.ServerConfig.AlipayInfo.NotifyUrl
	// 支付成功后的跳转页面
	p.ReturnURL = global.ServerConfig.AlipayInfo.ReturnUrl
	// 订单名称
	p.Subject = "优果生鲜订单-" + res.OrderSn
	// 唯一订单id
	p.OutTradeNo = res.OrderSn
	// 订单中需支付的总金额
	p.TotalAmount = strconv.FormatFloat(float64(res.Total), 'f', 2, 64)
	// 网页支付必须这样来写
	p.ProductCode = "FAST_INSTANT_TRADE_PAY"

	url, err := client.TradePagePay(p)
	if err != nil {
		zap.S().Error("生成支付url失败：", err)
		ctx.JSON(http.StatusInternalServerError, gin.H{
			"msg": "内部错误: 生成支付url失败",
		})
		return
	}

	ctx.JSON(http.StatusOK, gin.H{
		"id":         res.Id,
		"alipay_url": url.String(),
	})
}
